package jrobutt;

import jrobutt.room.menu.titlescreen.TitleScreenRoom;

import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.swing.*;
import java.util.*;
import java.util.List;

interface GameoverListener
{
    public void gameIsOver ();
}

public class JRobuttComponent extends Component implements Runnable, KeyListener
{
    public static final int NANOS_PER_ONE = 1000000000;
    public static final int NANOS_PER_MILLIS = 1000000;
    
    boolean gameover;
    GameoverListener gameoverListener;
    int sleeptimenanos;
    Dimension size;
    BufferedImage buffer;
    Graphics2D offscreen;
    Set<Integer> keys_down;
    Set<Integer> prev_keys_down;
    
    GameRoom current_room;
    
    public GameoverListener getGameoverListener ()
    {
        return this.gameoverListener;
    }
    public void setGameoverListener (GameoverListener listener)
    {
        this.gameoverListener = listener;
    }
    
    public void init (Dimension size)
    {
        this.size = size;
        gameover = false;
        
        keys_down = new HashSet<Integer>();
        prev_keys_down = new HashSet<Integer>();
        
        buffer = new BufferedImage(size.width, size.height, BufferedImage.TYPE_INT_ARGB);
        offscreen = buffer.createGraphics();
        offscreen.setBackground(Settings.bgcolor);
        offscreen.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        
        sleeptimenanos = (int)((float)NANOS_PER_ONE / Settings.fps);
        
        this.setFocusable(true);
        this.addKeyListener(this);
        new Thread(this).start();
    }
    
    public void keyTyped (KeyEvent kev) { }
    public void keyPressed (KeyEvent kev)
    {
        synchronized (keys_down)
        {
            keys_down.add(kev.getKeyCode());
        }
    }
    public void keyReleased (KeyEvent kev)
    {
        synchronized (keys_down)
        {
            keys_down.remove(kev.getKeyCode());
        }
    }
    
    public void run ()
    {
        current_room = new TitleScreenRoom();
        current_room.enter();
        try
        {
            do_game_loop();
        }
        catch (Throwable ex)
        {
            ex.printStackTrace();
            Common.show_error(ex,
                              "A fatal error has occurred",
                              "Please copy and email this error to sfoley1988@gmail.com",
                              new String[]{"JRobutt version " + Settings.vstring});
        }
        current_room.exit();
        if (gameoverListener != null) gameoverListener.gameIsOver();
    }
    private void do_game_loop ()
    {
        gameloop:
        while (!gameover)
        {
            long last_time = System.nanoTime();
            
            GameRoom next_room;
            // set up keyboard stuff
            Set<Integer> curr_keys_down;
            synchronized (keys_down)
            {
                curr_keys_down = new HashSet<Integer>(keys_down);
            }
            for (int key : curr_keys_down)
            {
                if (prev_keys_down.contains(key)) continue;
                next_room = current_room.key_pressed(key);
                if (next_room == Global.gameover_room)
                {
                    gameover = true;
                    break;
                }
                if (next_room != null)
                {
                    current_room.exit();
                    current_room = next_room;
                    current_room.enter();
                    synchronized (keys_down)
                    {
                        keys_down.clear();
                    }
                    prev_keys_down.clear();
                    continue gameloop;
                }
            }
            for (int key : prev_keys_down)
            {
                if (curr_keys_down.contains(key)) continue;
                next_room = current_room.key_released(key);
                if (next_room == Global.gameover_room)
                {
                    gameover = true;
                    break;
                }
                if (next_room != null)
                {
                    current_room.exit();
                    current_room = next_room;
                    current_room.enter();
                    synchronized (keys_down)
                    {
                        keys_down.clear();
                    }
                    prev_keys_down.clear();
                    continue gameloop;
                }
            }
            prev_keys_down = curr_keys_down;
            
            // udpate
            next_room = current_room.update();
            if (next_room == Global.gameover_room)
            {
                gameover = true;
                break;
            }
            if (next_room != null)
            {
                current_room.exit();
                current_room = next_room;
                current_room.enter();
                synchronized (keys_down)
                {
                    keys_down.clear();
                }
                prev_keys_down.clear();
                continue;
            }
            
            // next 3 lines: render
            Color bgcolor = current_room.get_bg_color();
            if (bgcolor != null)
            {
                offscreen.setColor(bgcolor);
                offscreen.clearRect(0, 0, size.width, size.height);
            }
            current_room.render(offscreen);
            this.repaint();
            
            long curr_time = System.nanoTime();
            int actual_sleeptimenanos = sleeptimenanos - (int)(curr_time - last_time);
            if (actual_sleeptimenanos <= 1) actual_sleeptimenanos = 1;
            int actual_sleeptimemillis = actual_sleeptimenanos / NANOS_PER_MILLIS;
            actual_sleeptimenanos -= actual_sleeptimemillis * NANOS_PER_MILLIS;
            try { Thread.sleep(actual_sleeptimemillis, actual_sleeptimenanos); }
            catch (InterruptedException ex) { }
        }
    }
    
    public void paint (Graphics g)
    {
        float target_aspect_ratio = (float)buffer.getWidth() / (float)buffer.getHeight();
        int target_w = getWidth();
        int target_h = getHeight();
        int w = getWidth();
        int h = getHeight();
        float aspect_ratio = (float)w / (float)h;
        if (aspect_ratio > target_aspect_ratio)
        {
            target_w = (int)((float)w / (aspect_ratio / target_aspect_ratio));
        }
        else
        {
            target_h = (int)((float)h * (aspect_ratio / target_aspect_ratio));
        }
        g.drawImage(buffer, (w - target_w) / 2, (h - target_h) / 2, target_w, target_h, null);
    }
    
    public void stop ()
    {
        gameover = true;
    }
}
